Other Ways to Secure Our Database Passwords
Let’s learn to secure the passwords in our database.
We'll cover the following
Hiding the password from SQL#
Now that we’re using a strong hashing function to encode the password before we store it, and we’re also using a salt to thwart dictionary attacks, we might think it enough to ensure security. But the password still appears in plain text in the SQL expression, which means that it’s readable if an attacker can intercept network packets or if SQL queries are logged, and the log files fall into the wrong hands.
We can protect against this kind of exposure by not putting the plain-text password into the SQL query. Instead, we can compute the hash in our application code, and use only this hash in the SQL query. It is not helpful for an attacker to intercept the hash because they can’t reverse it to get the password.
It is important to remember that we need the salt before we can compute the hash.
The following PHP example uses the PDO
extension to get the salt, compute a hash, and run a query to validate the password against the salted hash stored in the database:
<?php
$password = 'xyzzy';
$stmt = $pdo->query(
"SELECT salt
FROM Accounts WHERE account_name = 'bill'");
$row = $stmt->fetch();
$salt = $row[0];
$hash = hash('sha256', $password . $salt);
$stmt = $pdo->query("
SELECT (password_hash = '$hash') AS password_matches; FROM Accounts AS a WHERE a.account_name = 'bill'");
$row = $stmt->fetch();
if ($row === false) { // account 'bill' does not exist } else { if (!$password_matches) { // password given was incorrect } }
?>
The hash()
function is guaranteed to return only hexadecimal digits, so there’s no risk of SQL injection (see the chapter SQL Injection).
In web applications, attackers can also intercept data on the network between the user’s browser and the webserver. When the user submits a login form, the browser sends their password in plain text to the server. We could protect against this by encoding the password into a hash in the user’s browser before sending the form data, but this is awkward because we need to retrieve the salt associated with that password before we can compute the correct hash. A good compromise is to use a secure HTTP connection whenever sending a password from the browser to the application.
Resetting the password instead of recovering the password#
Now that the password is stored more securely, we still need to solve the original objective: help users who have forgotten their password. We can’t recover their password because now our database stores a hash instead of the password. We can’t reverse the hash any more easily than an attacker could. But we can allow a user access in other ways. Two sample implementations are described here.
The first alternative is to send an email with a temporary password generated by the application instead of emailing their password to a user when they have forgotten their password and requested help. For additional security, the application may also expire the temporary password after a short time, so if the email is intercepted, it’s more likely that it will not allow unauthorized access. Also, the application should be designed so that the user is forced to change the password as their first action when they log in.
Example of Email with a System-Generated Temporary Password
From: daemon
To: jill@example.com
Subject: password reset
You requested to reset your password for your account.
Your temporary password is “p0trz3b1e”.
This password will cease to allow access after one hour.
Click the link below to log in to your account and set your new password:
http://www.example.com/login
In a second alternative, instead of including a new password in an email, the request is logged in a database table and assigned a unique token as an identifier:
We can then include this token in an email, or even send it via another medium, such as SMS, as long as the recipient’s address is already associated with the account requesting a password reset. That way, if a stranger requests a password reset illicitly, it sends a spurious email only to the actual owner of the account.
Example of email with a temporary link to a password reset page
From: daemon
To: bill@example.com
Subject: password reset
You requested to reset your password for your account.
Click the link below within one hour to change your password.
After one hour, the link below will no longer work and your password will remain unchanged.
http://www.example.com/reset_password?token=f5cabff22532bd0025118905bdea50da
When the application receives a request for the special reset_password
screen, the value in the token
parameter must match a row in the PasswordResetRequest
table, and the expiration
timestamp on this row must still be upcoming. The account_id
on this row references the Accounts
table, so the token is restricted to enable a password reset of only one specific account.
Of course, it would be harmful if the wrong people could access this page. Simple restrictions reduce this risk, such as giving the special screen a short expiration period and making sure the screen does not show the account for which the password is being set.
The state of cryptography is constantly advancing and trying to stay ahead of attack technology. The techniques in this chapter will improve a great number of typical applications, but if we want to develop very secure systems, we should move on to more advanced techniques such as PBKDF2, which is a widely used key strengthening standard, and Bcrypt, which implements an adaptive hashing function.